﻿using System;
using System.Text;
using System.Globalization;

namespace XamlTorrent.Common
{
    /// <summary>
    /// Represents hexadecimal (base-16) character encoding.
    /// </summary>
    public class HexEncoding : Encoding
    {
        public HexEncoding()
            : base()
        {
        }

        public override int GetByteCount(char[] chars, int index, int count)
        {
            return (count / 2); // Hexadecimal uses 2 chars per byte
        }

        public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
        {
            for (int i = 0; i < charCount / 2; i++)
            {
                int lowCharIndex = (charIndex + (i * 2) + 1);
                int highCharIndex = (charIndex + (i * 2));

                byte low = AsciiToHex(chars[lowCharIndex]);
                byte high = AsciiToHex(chars[highCharIndex]);

                bytes[byteIndex + i] = (byte)(high * 0x10 + low);
            }

            return (charCount / 2);
        }

        private static byte AsciiToHex(char ch)
        {
            UInt16 c = ch;

            if (c >= 0x30 && c <= 0x39) // digit
                c -= 0x30;
            else if (c >= 0x41 && c <= 0x46) // upper-case A...F
                c -= 0x37;
            else if (c >= 0x61 && c <= 0x66) // lower-case a...f
                c -= 0x57;
            else
                throw new ArgumentOutOfRangeException("ch", "Non-hexadecimal character.");

            return (byte)c;
        }

        private static char HexToAscii(byte bt)
        {
            if (bt < 0xA) // digit
                return (char)(bt + 0x30);
            else
                return (char)(bt + 0x57);
        }

        public override int GetCharCount(byte[] bytes, int index, int count)
        {
            return (count * 2); // Hexadecimal uses 2 chars per byte
        }

        public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
        {
            for (int i = 0; i < byteCount; i++)
            {
                byte b = bytes[i];
                
                byte low = (byte)(b % 0x10);
                byte high = (byte)(b / 0x10);

                int lowCharIndex = (charIndex + (i * 2) + 1);
                int highCharIndex = (charIndex + (i * 2));

                chars[lowCharIndex] = HexToAscii(low);
                chars[highCharIndex] = HexToAscii(high);
            }

            return (byteCount * 2);
        }

        public override int GetMaxByteCount(int charCount)
        {
            return (charCount / 2); // Hexadecimal uses 2 chars per byte
        }

        public override int GetMaxCharCount(int byteCount)
        {
            return (byteCount * 2); // Hexadecimal uses 2 chars per byte
        }
    }
}
